#ifndef __PROCESS_PROTOCOL__
#define __PROCESS_PROTOCOL__
#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <clocale>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string>
#include <iostream>
#include <exception>
#include <thread>
#include <mutex>
#define BUFF_SIZE 512
namespace secret_proc_protocol
{
enum protocolErrMsg
{
    PROC_BAD_MKFIFO,
    PROC_BAD_OPEN,
    PROC_BAD_CLOSE,
    PROC_BAD_WRITE,
    PROC_BAD_READ,
    PROC_BAD_REMOVE,
    PROC_BAD_FILETYPE,
    PROC_NONE       // NONE表示没有异常，只在protocolException默认初始化时使用
};
};

class protocolException
{
public:
    protocolException();
    protocolException(const secret_proc_protocol::protocolErrMsg error_message, const int proc_errno);
    const std::string what() const;
private:
    int proc_errno;
    secret_proc_protocol::protocolErrMsg errmsg;
    std::string errmsg_str;
};

enum accessMode 
{
    BLOCKED,
    UNBLOCKED,
    ASYNCHRONIZED
};

class pipeReader
{
public:
    pipeReader(const std::string& pipe_filename, accessMode mode);
};

class pipeWriter
{
public:
    pipeWriter(const std::string& pipe_filename, accessMode mode);
};

/**
 * @brief procProtocol can read and write with a certain
 *  pipe file and set a particular process id to send 
 * infomation.
 * @author Ethan Yao
 **/
class procProtocol
{
public:
    procProtocol(bool RAII_remove=true):RAII_remove(RAII_remove), is_init(false){}
    // To ensure the exception works, use the upper constructor and init to initialization
    procProtocol(const std::string &write_filename, const std::string &read_filename, bool RAII_remove=true);
    void init(const std::string &write_filename, const std::string &read_filename);
    std::string readPipe(accessMode mode=BLOCKED);
    void writePipe(const std::string& input, accessMode mode=BLOCKED);
    ~procProtocol();
private:
    std::string server_filename, client_filename;
    std::string buffer;
    struct stat server_stat, client_stat;
    int proc_errno;
    int fd;                     // short for file descriptor
    int ch_num;
    bool RAII_remove, is_init;
};

class communicator : protected procProtocol
{
public:
    ~communicator();
    void createReadThread();
    void createWriteThread();
    std::string get_read_msg(){std::lock_guard<std::mutex> guard(read_mutex);return read_msg;}
    void set_write_msg(const std::string& write_msg)\
        {std::lock_guard<std::mutex> guard(write_mutex);this->write_msg = write_msg;}
private:
    bool read_stop=false, write_stop=false;
    std::string read_msg;
    std::string write_msg;
    std::thread read_thread;
    std::thread write_thread;
    std::mutex read_mutex;
    std::mutex write_mutex;
};

#endif // __PROCESS_PROTOCOL__